import React, { useRef, useState, useEffect, forwardRef, useImperativeHandle } from "react";
import { Canvas, useLoader, useFrame } from "@react-three/fiber";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
import { AnimationMixer, AnimationClip, AmbientLight } from "three";
import { OrbitControls } from "@react-three/drei";
import * as THREE from 'three';

// 定义 AnimatedModel 组件的属性类型
// modelPath: 必传，GLTF 模型文件的路径
// scale: 可选，模型的缩放比例，默认值为 [1, 1, 1]
// position: 可选，模型在场景中的位置，默认值为 [0, 0, 0]
// onActionSwitch: 必传，当动画动作切换时触发的回调函数，接收动作名称作为参数
interface AnimatedModelProps {
    modelPath: string;
    scale?: [number, number, number];
    position?: [number, number, number];
    onActionSwitch: (actionName: string) => void; 
}

// 使用 forwardRef 包裹组件，使得外部组件可以通过 ref 调用该组件内部的方法
const AnimatedModel = forwardRef<{ switchAction: (actionName: string) => void }, AnimatedModelProps>(({
    modelPath,
    scale = [1, 1, 1],
    position = [0, 0, 0],
    onActionSwitch
}, ref) => {
    // 使用 useLoader 钩子加载 GLTF 模型，GLTFLoader 是 Three.js 提供的用于加载 GLTF 格式文件的加载器
    const gltf = useLoader(GLTFLoader, modelPath);
    // 使用 useRef 存储 AnimationMixer 实例，初始值为 null
    // AnimationMixer 用于混合和播放模型的动画
    const mixer = useRef<AnimationMixer | null>(null);
    // 使用 useRef 存储每个动画剪辑对应的 AnimationAction，初始值为空对象
    const actions = useRef<{ [key: string]: THREE.AnimationAction }>({});
    // 使用 useRef 存储当前正在播放的动画动作，初始值为 null
    const currentAction = useRef<THREE.AnimationAction | null>(null);

    // 当动画数据加载完成或更新时，执行动画初始化和清理操作
    useEffect(() => {
        // 初始化 AnimationMixer，传入模型的场景对象
        mixer.current = new AnimationMixer(gltf.scene);
        
        // 获取模型的所有动画剪辑
        const clips = gltf.animations;
        // 遍历每个动画剪辑，为每个剪辑创建对应的 AnimationAction 并存储在 actions 中
        clips.forEach((clip) => {
            if (mixer.current) {
                const action = mixer.current.clipAction(clip);
                actions.current[clip.name] = action;
            }
        });

        // 默认播放第一个动画
        if (clips.length > 0) {
            const defaultAction = actions.current[clips[0].name];
            if (defaultAction) {
                currentAction.current = defaultAction;
                defaultAction.play();
            }
        }

        // 组件卸载时的清理操作
        // 停止所有动画动作并清理 mixer
        return () => {
            if (mixer.current) {
                clips.forEach(clip => {
                    const action = mixer.current!.clipAction(clip);
                    action.stop();
                });
                mixer.current.stopAllAction();
            }
        };
    }, [gltf.animations]); // 当动画数据改变时，重新运行该副作用

    // 定义切换动画动作的方法
    const switchAction = (actionName: string) => {
        // 根据动作名称从 actions 中获取对应的 AnimationAction
        const action = actions.current[actionName];
        if (action) {
            // 停止并重置当前正在播放的动画动作
            if (currentAction.current) {
                currentAction.current.stop();
                currentAction.current.reset();
            }
            // 播放新的动画动作
            currentAction.current = action;
            action.reset().play();
            // 调用外部传入的回调函数，通知动画动作已切换
            onActionSwitch(actionName);
        } else {
            // 如果要切换的动画动作不存在，打印错误信息
            console.error(`Action "${actionName}" not found!`);
        }
    };

    // 使用 useImperativeHandle 暴露 switchAction 方法，使得外部组件可以通过 ref 调用
    useImperativeHandle(ref, () => ({
        switchAction
    }));

    // 使用 useFrame 钩子，在每一帧更新动画
    // 每一帧都会调用该函数，更新 AnimationMixer 的状态
    useFrame((_, delta) => {
        mixer.current?.update(delta);
    });

    // 设置场景光照，添加环境光
    useEffect(() => {
        // 创建一个白色的环境光，强度为 1
        const ambientLight = new AmbientLight(0xffffff, 1);
        // 将环境光添加到模型的场景中
        gltf.scene.add(ambientLight);
        // 组件卸载时，移除环境光
        return () => {
            gltf.scene.remove(ambientLight);
        };
    }, [gltf.scene]);

    // 应用缩放和位置变换到模型场景
    gltf.scene.scale.set(...scale);
    gltf.scene.position.set(...position);

    // 渲染模型场景，primitive 组件用于将 Three.js 对象渲染到场景中
    return <primitive object={gltf.scene} />;
});

// 定义 GLBViewer 组件，用于展示包含动画的 3D 模型
const GLBViewer: React.FC = () => {
    // 使用 useRef 创建一个 ref 对象，用于引用 AnimatedModel 组件
    const modelRef = useRef<{ switchAction: (actionName: string) => void }>(null);

    return (
        <>
            {/* 创建 3D 场景，设置相机的初始位置和视角 */}
            <Canvas camera={{ position: [0, 0, 5], fov: 50 }}>
                {/* 添加环境光，照亮整个场景，强度为 0.5 */}
                <ambientLight intensity={0.5} />
                {/* 添加平行光，设置其位置和强度，增强场景的光照效果 */}
                <directionalLight position={[10, 10, 5]} intensity={1} />
                
                {/* 使用 AnimatedModel 组件加载并渲染模型 */}
                <AnimatedModel
                    ref={modelRef}
                    modelPath="/无标题1.glb"
                    position={[-0.5, -3, -1]}
                    scale={[3, 3, 3]}
                    // 当动画动作切换时，打印当前的动画动作名称
                    onActionSwitch={(actionName) => console.log("Current action:", actionName)}
                />
                
                {/* 添加轨道控制器，允许用户通过鼠标交互来控制场景视角，支持平移、缩放和旋转 */}
                <OrbitControls enablePan={true} enableZoom={true} enableRotate={true} />
            </Canvas>

            {/* 创建一个包含动画切换按钮的 div，位于页面左上角 */}
            <div style={{ position: 'absolute', top: 20, left: 20 }}>
                {/* 点击按钮时，调用 AnimatedModel 组件的 switchAction 方法，切换到指定的动画动作 */}
                <button onClick={() => modelRef.current?.switchAction('坐_Armature.001')}>坐</button>
                <button onClick={() => modelRef.current?.switchAction('跪_Armature.002')}>跪</button>
                <button onClick={() => modelRef.current?.switchAction('沮丧_Armature.003')}>沮丧</button>
                <button onClick={() => modelRef.current?.switchAction('跳')}>跳跃</button>
            </div>
        </>
    );
};

// 导出 GLBViewer 组件，供其他文件使用
export default GLBViewer;